package org.jetnuke.modules.blog.utils;

import org.apache.log4j.Logger;

import java.net.HttpURLConnection;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.XmlRpcHandler;
import org.jetnuke.dao.BlogCommentDAO;
import org.jetnuke.dao.BlogPostDAO;
import org.jetnuke.dao.DataAccessDriver;
import org.jetnuke.entities.BlogComment;
import org.jetnuke.entities.BlogPost;
import org.jetnuke.util.StringUtils;

public class PingBackHandler implements XmlRpcHandler {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger
			.getLogger(PingBackHandler.class);

	protected static final int PINGBACK_GENERIC_FAULT_CODE = 0;
	protected static final int PINGBACK_SOURCE_URI_NON_EXISTENT_CODE = 16;
	protected static final int PINGBACK_NO_LINK_TO_TARGET_URI_CODE = 17;
	protected static final int PINGBACK_TARGET_URI_NON_EXISTENT_CODE = 32;
	protected static final int PINGBACK_TARGET_URI_NOT_ENABLED_CODE = 33;
	protected static final int PINGBACK_ALREADY_REGISTERED_CODE = 48;
	protected static final int PINGBACK_ACCESS_DENIED_CODE = 49;
	protected static final int PINGBACK_UPSTREAM_SERVER_ERROR_CODE = 50;

	private HttpServletRequest request;

	public PingBackHandler(HttpServletRequest request) {
		this.request = request;
	}

	public String ping(String sourceURI, String targetURI)
			throws XmlRpcException {
		if (logger.isDebugEnabled()) {
			logger.debug("ping(String, String) - start"); //$NON-NLS-1$
		}

		HttpClient client = new HttpClient();
		GetMethod gm = new GetMethod(sourceURI);
		client.setConnectionTimeout(8000);
		String sourcePage = null;
		try {
			client.executeMethod(gm);
			sourcePage = gm.getResponseBodyAsString();
		} catch (Throwable e) {
			logger.error("ping(String, String)", e); //$NON-NLS-1$

			throw new XmlRpcException(PINGBACK_GENERIC_FAULT_CODE,
					"Unable to retrieve source URI");
		}

		if (sourcePage.indexOf(targetURI) == -1) {

			throw new XmlRpcException(PINGBACK_NO_LINK_TO_TARGET_URI_CODE,
					"Target URI not found in source URI");
		}
		HeadMethod hm = new HeadMethod(targetURI);
		try {
			int returnValue = client.executeMethod(hm);
			if (returnValue != HttpURLConnection.HTTP_OK) {
				throw new Exception();
			}
		} catch (Throwable t) {
			logger.error("ping(String, String)", t); //$NON-NLS-1$

			throw new XmlRpcException(PINGBACK_TARGET_URI_NON_EXISTENT_CODE,
					"Target URI does not exist");
		}
		int targetIndex = targetURI.indexOf("id=");
		String ID = targetURI.substring(targetIndex + 3);
		int id = -1;
		BlogPost bp = null;
		try {
			id = Integer.parseInt(ID);
			BlogPostDAO bpm = DataAccessDriver.getInst().newBlogPostDAO();
			bp = bpm.retrieveByPK(id);
			if (bp.getID() != id) {
				throw new Exception();
			}

		} catch (Throwable t) {
			logger.error("ping(String, String)", t); //$NON-NLS-1$

			throw new XmlRpcException(PINGBACK_TARGET_URI_NON_EXISTENT_CODE,
					"Target URI does not exist");
		}
		if (!"open".equals(bp.getPINGSTATUS())) {
			throw new XmlRpcException(PINGBACK_TARGET_URI_NOT_ENABLED_CODE,
					"Target URI does not support pingbacks");
		}
		BlogCommentDAO bcm = DataAccessDriver.getInst().newBlogCommentDAO();
		List commentByPostList = null;
		try {
			bcm.selectByPostID(id);
			Iterator it = commentByPostList.iterator();
			while (it.hasNext()) {
				BlogComment bc = (BlogComment) it.next();
				if ("pingback".equals(bc.getTYPE())
						&& sourceURI.equals(bc.getAUTHOR_URL())) {
					throw new XmlRpcException(PINGBACK_ALREADY_REGISTERED_CODE,
							"The pingback has already been registered.");
				}
			}
		} catch (Exception e) {
			logger.warn("ping(String, String) - exception ignored", e); //$NON-NLS-1$

		}

		BlogComment bc = new BlogComment();
		String ua = request.getHeader("user-agent");
		if (ua == null)
			ua = "";
		String ip = request.getRemoteAddr();
		if (ip == null)
			ip = "";
		bc.setAUTHOR("");
		bc.setAGENT(ua);

		bc.setAUTHOR_EMAIL("");
		bc.setAUTHOR_IP(ip);
		bc.setAUTHOR_URL(sourceURI);
		String comment = getExcerptFromSource(sourcePage, targetURI);
		bc.setCONTENT("[...]" + comment + "[...]");
		bc.setDATE(new GregorianCalendar().getTime());
		bc.setPARENT(0);
		bc.setPOST_ID(id);
		bc.setTYPE("pingback");
		int uid = 1;

		String approved = "0";
		// if(uid==bp.getAID())
		approved = "1"; // TODO give more flexibility!!
		bc.setAPPROVED(approved);
		bc.setUSER_ID(uid);

		try {
			bcm.addNew(bc);
		} catch (Exception e) {
			logger.error("ping(String, String)", e); //$NON-NLS-1$

			throw new XmlRpcException(PINGBACK_GENERIC_FAULT_CODE,
					"Unable to retrieve source URI");
		}
		String returnString = "Registered pingback from: " + sourceURI
				+ " to: " + targetURI;
		if (logger.isDebugEnabled()) {
			logger.debug("ping(String, String) - end"); //$NON-NLS-1$
		}
		return returnString;
	}

	public String ping(Vector values) throws XmlRpcException {
		if (logger.isDebugEnabled()) {
			logger.debug("ping(Vector) - start"); //$NON-NLS-1$
		}

		if (values.size() < 2 || values.get(0) == null
				|| ((String) values.get(0)).trim().length() == 0) {
			throw new XmlRpcException(PINGBACK_SOURCE_URI_NON_EXISTENT_CODE,
					"Pingback must include a source URI");
		}
		String returnString = ping((String) values.get(0), (String) values
				.get(1));
		if (logger.isDebugEnabled()) {
			logger.debug("ping(Vector) - end"); //$NON-NLS-1$
		}
		return returnString;

	}

	protected String getExcerptFromSource(String source, String targetURI) {
		if (logger.isDebugEnabled()) {
			logger.debug("getExcerptFromSource(String, String) - start"); //$NON-NLS-1$
		}

		String excerpt = null;

		int startOfTarget = source.indexOf(targetURI);
		if (startOfTarget != -1) {
			int startOfExcerpt = startOfTarget - 200;
			if (startOfExcerpt < 0) {
				startOfExcerpt = 0;
			}

			int endOfExcerpt = startOfTarget + 200;
			if (endOfExcerpt > source.length()) {
				endOfExcerpt = source.length();
			}

			excerpt = source.substring(startOfExcerpt, endOfExcerpt);
			excerpt = StringUtils.filterText(excerpt, true);
		}

		if (logger.isDebugEnabled()) {
			logger.debug("getExcerptFromSource(String, String) - end"); //$NON-NLS-1$
		}
		return excerpt;
	}

	public Object execute(String arg0, Vector values) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("execute(String, Vector) - start"); //$NON-NLS-1$
		}

		Object returnObject = ping(values);
		if (logger.isDebugEnabled()) {
			logger.debug("execute(String, Vector) - end"); //$NON-NLS-1$
		}
		return returnObject;
	}
}
